package org.flexthinker.binding.form.support
{
    import flash.events.Event;

    import org.flexthinker.binding.value.ValueModel;

    import org.flexthinker.binding.value.support.BufferedValueModel;

    import mx.events.PropertyChangeEvent;

    /**
     * Default form model implementation.
     */
    public class DefaultFormModel extends AbstractFormModel
    {
        /**
         * Name of the event dispatched when the form changes its dirty state.
         */
        public static const DIRTY_STATE_CHANGED:String = "dirtyStateChanged";

        private var dirtyPropertyValueModels:Vector.<ValueModel> = new Vector.<ValueModel>();

        public function DefaultFormModel(domainObject:Object, buffered:Boolean = true):void
        {
            // it should be always buffered in order to check for isDirty
            super(domainObject, buffered);
        }


        override public function isDirty():Boolean
        {
            return dirtyPropertyValueModels.length > 0;
        }

        /**
         * Fires the necessary property change event for changes to the dirty
         * property. Must be called whenever the value of dirty is changed.
         */
        protected function dirtyUpdated():void
        {
            dispatchEvent(new Event(DIRTY_STATE_CHANGED));
        }

        override protected function preProcessNewValueModel(formProperty:String, valueModel:ValueModel):ValueModel
        {

            if (! (valueModel is ValidatingFormValueModel))
            {
                return new ValidatingFormValueModel(formProperty, valueModel, true, propertyChange);
            }
            return valueModel;
        }


        override protected function postProcessNewValueModel(formProperty:String, valueModel:ValueModel):void
        {
            validateAfterPropertyChanged(formProperty);
        }


        private function propertyChange(formProperty:String, event:PropertyChangeEvent):void
        {
            validateAfterPropertyChanged(formProperty, event);
        }

        protected function validateAfterPropertyChanged(formProperty:String, event:PropertyChangeEvent = null):void
        {
            if (formProperty)
            {
                invalidateDirtyFlagForProperty(formProperty);
            }
        }

        private function invalidateDirtyFlagForProperty(formProperty:String):void
        {
            var currentValueModel:ValidatingFormValueModel = ValidatingFormValueModel(getValueModel(formProperty));
            var idx:int = dirtyPropertyValueModels.indexOf(currentValueModel);

            if (currentValueModel.getWrappedValueModel() is BufferedValueModel)
            {
                var bvm:BufferedValueModel = BufferedValueModel(currentValueModel.getWrappedValueModel());
                if (bvm.isBuffering() && idx < 0)
                {
                    dirtyPropertyValueModels.push(currentValueModel);
                    dirtyUpdated();
                }
                if (!bvm.isBuffering() && idx >= 0)
                {
                    dirtyPropertyValueModels.splice(idx, 1);
                    dirtyUpdated();
                }
            }
        }


        override protected function postCommit():void
        {
            super.postCommit();
            var vm:ValidatingFormValueModel;
            //clear dirtyPropertyValueModels
            for ( var i:int=0; i < dirtyPropertyValueModels.length; i++ )
            {
                vm = ValidatingFormValueModel(dirtyPropertyValueModels[i]) ;
                if ( vm.getWrappedValueModel() is BufferedValueModel )
                {
                    if ( ! BufferedValueModel(vm.getWrappedValueModel()).isBuffering() )
                    {
                        dirtyPropertyValueModels.splice(i,1);
                        i--;
                    }
                }
            }

            dirtyUpdated();
        }
    }
}

import org.flexthinker.binding.value.PropertyChangeListener;
import org.flexthinker.binding.value.ValueModel;
import org.flexthinker.binding.value.ValueModelProxy;
import org.flexthinker.binding.value.support.AbstractValueModelProxy;
import org.flexthinker.binding.value.support.DelegatingPropertyChangeListener;

import mx.events.PropertyChangeEvent;

class ValidatingFormValueModel extends AbstractValueModelProxy implements ValueModel, ValueModelProxy
{
    private var valueChangeListener:PropertyChangeListener;
    private var formProperty:String;
    private var valueChangeClosure:Function;

    public function ValidatingFormValueModel(formProperty:String, model:ValueModel, validateOnChange:Boolean,
            valueChangeClosure:Function):void
    {
        super(model);
        this.formProperty = formProperty;
        this.valueChangeClosure = valueChangeClosure;
        init(validateOnChange);
    }

    private function init(validateOnChange:Boolean):void
    {
        if (validateOnChange)
        {
            valueChangeListener = new DelegatingPropertyChangeListener(valueChangeHandler);
            addValueChangeListener(valueChangeListener);
        }
    }

    public function getFormProperty():String
    {
        return formProperty;
    }

    private function valueChangeHandler(event:PropertyChangeEvent):void
    {
        valueChangeClosure(formProperty, event);
    }

}